# Copyright 2012 OpenStack Foundation
# 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.

from configs import config
from lib import auth
from lib import exceptions as lib_exc
from lib.services import clients
from lib.services.loadbalancer.flava_lb_client import FlavaLbClient
from lib.services.loadbalancer.verda_lb_client import VerdaLbClient
from lib.services.vpc.verda_vpc_client import VerdaVpcClient
from lib.services.vpc.flava_vpc_client import FlavaVpcClient
from lib.services.faas.flava_available_client import FlavaAvailableClient
from lib.services.faas.flava_functions_client import FlavaFunctionsClient
from lib.services.faas.flava_templates_client import FlavaTemplatesClient
from lib.services.faas.flava_github_user_client import FlavaGithubUserClient
from lib.services.faas.flava_event_source_client import FlavaEventSourceClient
from lib.services.kaas.flava_clusters_client import FlavaClustersClient
from lib.services.kaas.flava_specs_client import FlavaSpecsClient
from lib.services.egress_proxy.flava_egress_proxy_client import FlavaEgressProxyClient
from lib.services.identity.v3.flava_sa_credentials_client import (
    FlavaSACredentialsClient,
)
from lib.services.container_registry.flava_container_registry_client import (
    FlavaContainerRegistryClient,
)
from lib.services.flava_pulsar.flava_pulsar_client import (
    FlavaPulsarClient,
)
from lib.services.dns.flava_dns_client import FlavaDnsClient
from lib.services.gslb.flava_gslb_client import FlavaGslbClient
from lib.services.langfuse.flava_langfuse_projects_client import (
    FlavaLangfuseProjectsClient,
)
from lib.services.langfuse.flava_admin_client import (
    FlavaLangfuseAdminClient,
)
from lib.services.langfuse.flava_available_client import (
    FlavaLangfuseAvailableClient,
)
from lib.services.api_gateway.apis_client import ApisClient as ApiGatewayApisClient
from lib.services.api_gateway.stages_client import StagesClient as ApiGatewayStagesClient
from lib.services.api_gateway.paths_client import PathsClient as ApiGatewayPathsClient
from lib.services.api_gateway.methods_client import MethodsClient as ApiGatewayMethodsClient
from lib.services.api_gateway.keys_client import KeysClient as ApiGatewayKeysClient
from lib.services.api_gateway.keysets_client import KeysetsClient as ApiGatewayKeysetsClient
from lib.services.api_gateway.projects_client import ProjectsClient as ApiGatewayProjectsClient
from lib.services.api_gateway.tags_client import TagsClient as ApiGatewayTagsClient
from lib.services.api_gateway.access_control_client import AccessControlClient as ApiGatewayAccessControlClient
from lib.services.api_gateway.monitoring_client import MonitoringClient as ApiGatewayMonitoringClient
from lib.services.api_gateway.staged_methods_client import StagedMethodsClient as ApiGatewayStagedMethodsClient
from lib.services.api_gateway.staged_paths_client import StagedPathsClient as ApiGatewayStagedPathsClient
from lib.services.api_gateway.flava_apis_client import FlavaApisClient
from lib.services.api_gateway.flava_stages_client import FlavaStagesClient
from lib.services.api_gateway.flava_paths_client import FlavaPathsClient
from lib.services.api_gateway.flava_methods_client import FlavaMethodsClient
from lib.services.api_gateway.flava_keys_client import FlavaKeysClient
from lib.services.api_gateway.flava_keysets_client import FlavaKeysetsClient
from lib.services.api_gateway.flava_projects_client import FlavaProjectsClient
from lib.services.api_gateway.flava_tags_client import FlavaTagsClient
from lib.services.api_gateway.flava_access_control_client import FlavaAccessControlClient
from lib.services.api_gateway.flava_monitoring_client import FlavaMonitoringClient
from lib.services.api_gateway.flava_staged_methods_client import FlavaStagedMethodsClient
from lib.services.api_gateway.flava_staged_paths_client import FlavaStagedPathsClient
from lib.services.servicemap.flava_project_client import FlavaProjectClient
from lib.services.servicemap.flava_service_client import FlavaServiceClient
from lib.services.servicemap.flava_service_group_client import FlavaServiceGroupClient
from lib.services.servicemap.flava_service_resource_client import FlavaServiceResourceClient
from lib.services.servicemap.flava_service_resource_group_client import FlavaServiceResourceGroupClient
from lib.services.servicemap.flava_service_relation_client import FlavaServiceRelationClient
from lib.services.servicemap import (
    ProjectClient as ServicemapProjectClient,
    ServiceClient as ServicemapServiceClient,
    ServiceGroupClient as ServicemapServiceGroupClient,
    ServiceResourceClient as ServicemapServiceResourceClient,
    ServiceResourceGroupClient as ServicemapServiceResourceGroupClient,
    ServiceRelationClient as ServicemapServiceRelationClient,
)
from lib.services.cloud_blueprint.metadata_client import MetadataClient
from lib.services.cloud_blueprint.projects_client import ProjectsClient
from lib.services.cloud_blueprint.deployments_client import DeploymentsClient
from lib.services.cloud_blueprint.runs_client import RunsClient
from lib.services.cloud_blueprint.run_tasks_client import RunTasksClient
from lib.services.cloud_blueprint.configuration_versions_client import (
    ConfigurationVersionsClient,
)
from lib.services.cloud_blueprint.github_client import GithubClient
from lib.services.cloud_blueprint.flava_metadata_client import FlavaMetadataClient
from lib.services.cloud_blueprint.flava_projects_client import FlavaProjectsClient as FlavaBlueprintProjectsClient
from lib.services.cloud_blueprint.flava_deployments_client import FlavaDeploymentsClient
from lib.services.cloud_blueprint.flava_runs_client import FlavaRunsClient
from lib.services.cloud_blueprint.flava_run_tasks_client import FlavaRunTasksClient
from lib.services.cloud_blueprint.flava_configuration_versions_client import (
    FlavaConfigurationVersionsClient,
)
from lib.services.cloud_blueprint.flava_github_client import FlavaGithubClient
import os

from oslo_log import log as logging


CONF = config.CONF
LOG = logging.getLogger(__name__)


class Manager(clients.ServiceClients):
    """Top level manager for OpenStack tempest clients"""

    def __init__(self, credentials, scope="project"):
        """Initialization of Manager class.

        Setup all services clients and make them available for tests cases.
        :param credentials: type Credentials or TestResources
        :param scope: default scope for tokens produced by the auth provider
        """
        _, identity_uri = get_auth_provider_class(credentials)
        super(Manager, self).__init__(
            credentials=credentials,
            identity_uri=identity_uri,
            scope=scope,
            region=CONF.identity.region,
        )
        # TODO(andreaf) When clients are initialised without the right
        # parameters available, the calls below will trigger a KeyError.
        # We should catch that and raise a better error.
        self._set_compute_clients()
        self._set_identity_clients()
        self._set_volume_clients()
        self._set_object_storage_clients()
        self._set_image_clients()
        self._set_network_clients()
        self._set_placement_clients()
        # Add new LB service clients register set methods
        self._set_lb_clients()
        # Add new VPC service clients register set methods
        self._set_vpc_clients()
        self._set_faas_clients()
        self._set_kaas_clients()
        self._set_egress_proxy_clients()
        self._set_container_registry_clients()
        self._set_pulsar_clients()
        self._set_dns_clients()
        self._set_gslb_clients()
        self._set_langfuse_clients()
        # Common params for non-catalog (BearerToken) clients.
        # Catalog-based clients get these via ServiceClients._parameters,
        # but direct-instantiated clients need them explicitly.
        self._bearer_client_params = {
            'region': CONF.identity.region,
            'disable_ssl_certificate_validation':
                CONF.identity.disable_ssl_certificate_validation,
        }

        self._set_servicemap_clients()
        self._set_redis_clients()
        self._set_api_gateway_clients()
        self._set_rollouts_clients()
        self._set_cloud_blueprint_clients()

        # TODO(andreaf) This is maintained for backward compatibility
        # with plugins, but it should removed eventually, since it was
        # never a stable interface and it's not useful anyways
        self.default_params = config.service_client_config()

    def _set_network_clients(self):
        self.network_agents_client = self.network.AgentsClient()
        self.network_extensions_client = self.network.ExtensionsClient()
        self.networks_client = self.network.NetworksClient()
        self.subnetpools_client = self.network.SubnetpoolsClient()
        self.subnets_client = self.network.SubnetsClient()
        self.ports_client = self.network.PortsClient()
        self.network_quotas_client = self.network.QuotasClient()
        self.floating_ips_client = self.network.FloatingIPsClient()
        self.floating_ips_port_forwarding_client = (
            self.network.FloatingIpsPortForwardingClient()
        )
        self.metering_labels_client = self.network.MeteringLabelsClient()
        self.metering_label_rules_client = self.network.MeteringLabelRulesClient()
        self.routers_client = self.network.RoutersClient()
        self.security_group_rules_client = self.network.SecurityGroupRulesClient()
        self.security_groups_client = self.network.SecurityGroupsClient()
        self.network_versions_client = self.network.NetworkVersionsClient()
        self.service_providers_client = self.network.ServiceProvidersClient()
        self.tags_client = self.network.TagsClient()
        self.qos_client = self.network.QosClient()
        self.qos_min_bw_client = self.network.QosMinimumBandwidthRulesClient()
        self.qos_limit_bw_client = self.network.QosLimitBandwidthRulesClient()
        self.qos_min_pps_client = self.network.QosMinimumPacketRateRulesClient()
        self.segments_client = self.network.SegmentsClient()
        self.trunks_client = self.network.TrunksClient()
        self.log_resource_client = self.network.LogResourceClient()
        self.loggable_resource_client = self.network.LoggableResourceClient()

    def _set_image_clients(self):
        if CONF.service_available.glance:
            self.image_client_v2 = self.image_v2.ImagesClient()
            self.image_member_client_v2 = self.image_v2.ImageMembersClient()
            self.image_cache_client = self.image_v2.ImageCacheClient()
            self.namespaces_client = self.image_v2.NamespacesClient()
            self.resource_types_client = self.image_v2.ResourceTypesClient()
            self.namespace_objects_client = self.image_v2.NamespaceObjectsClient()
            self.schemas_client = self.image_v2.SchemasClient()
            self.namespace_properties_client = self.image_v2.NamespacePropertiesClient()
            self.namespace_tags_client = self.image_v2.NamespaceTagsClient()
            self.image_versions_client = self.image_v2.VersionsClient()
            self.tasks_client = self.image_v2.TaskClient()
            # NOTE(danms): If no alternate endpoint is configured,
            # this client will work the same as the base self.images_client.
            # If your test needs to know if these are different, check the
            # config option to see if the alternate_image_endpoint is set.
            self.image_client_remote = self.image_v2.ImagesClient(
                service=CONF.image.alternate_image_endpoint,
                endpoint_type=CONF.image.alternate_image_endpoint_type,
                region=CONF.image.region,
            )

    def _set_compute_clients(self):
        self.agents_client = self.compute.AgentsClient()
        self.compute_networks_client = self.compute.NetworksClient()
        self.migrations_client = self.compute.MigrationsClient()
        self.security_group_default_rules_client = (
            self.compute.SecurityGroupDefaultRulesClient()
        )
        self.certificates_client = self.compute.CertificatesClient()
        eip = CONF.compute_feature_enabled.enable_instance_password
        self.servers_client = self.compute.ServersClient(enable_instance_password=eip)
        self.server_groups_client = self.compute.ServerGroupsClient()
        self.limits_client = self.compute.LimitsClient()
        self.keypairs_client = self.compute.KeyPairsClient(
            ssh_key_type=CONF.validation.ssh_key_type
        )
        self.quotas_client = self.compute.QuotasClient()
        self.quota_classes_client = self.compute.QuotaClassesClient()
        self.flavors_client = self.compute.FlavorsClient()
        self.extensions_client = self.compute.ExtensionsClient()
        self.compute_floating_ips_client = self.compute.FloatingIPsClient()
        self.compute_security_group_rules_client = (
            self.compute.SecurityGroupRulesClient()
        )
        self.compute_security_groups_client = self.compute.SecurityGroupsClient()
        self.interfaces_client = self.compute.InterfacesClient()
        self.availability_zone_client = self.compute.AvailabilityZoneClient()
        self.aggregates_client = self.compute.AggregatesClient()
        self.services_client = self.compute.ServicesClient()
        self.tenant_usages_client = self.compute.TenantUsagesClient()
        self.hosts_client = self.compute.HostsClient()
        self.hypervisor_client = self.compute.HypervisorClient()
        self.instance_usages_audit_log_client = (
            self.compute.InstanceUsagesAuditLogClient()
        )
        self.tenant_networks_client = self.compute.TenantNetworksClient()
        self.assisted_volume_snapshots_client = (
            self.compute.AssistedVolumeSnapshotsClient()
        )
        self.server_external_events_client = self.compute.ServerExternalEventsClient()

        # NOTE: The following client needs special timeout values because
        # the API is a proxy for the other component.
        params_volume = {
            "build_interval": CONF.volume.build_interval,
            "build_timeout": CONF.volume.build_timeout,
        }
        self.volumes_extensions_client = self.compute.VolumesClient(**params_volume)
        self.compute_versions_client = self.compute.VersionsClient(**params_volume)
        self.snapshots_extensions_client = self.compute.SnapshotsClient(**params_volume)
        self.compute_images_client = self.compute.ImagesClient(
            build_timeout=CONF.image.build_timeout
        )

    def _set_placement_clients(self):
        self.placement_client = self.placement.PlacementClient()
        self.resource_providers_client = self.placement.ResourceProvidersClient()

    def _set_identity_clients(self):
        # Clients below use the admin endpoint type of Keystone API v2
        params_v2_admin = {"endpoint_type": CONF.identity.v2_admin_endpoint_type}
        self.endpoints_client = self.identity_v2.EndpointsClient(**params_v2_admin)
        self.identity_client = self.identity_v2.IdentityClient(**params_v2_admin)
        self.tenants_client = self.identity_v2.TenantsClient(**params_v2_admin)
        self.roles_client = self.identity_v2.RolesClient(**params_v2_admin)
        self.users_client = self.identity_v2.UsersClient(**params_v2_admin)
        self.identity_services_client = self.identity_v2.ServicesClient(
            **params_v2_admin
        )

        # Clients below use the public endpoint type of Keystone API v2
        params_v2_public = {"endpoint_type": CONF.identity.v2_public_endpoint_type}
        self.identity_public_client = self.identity_v2.IdentityClient(
            **params_v2_public
        )
        self.tenants_public_client = self.identity_v2.TenantsClient(**params_v2_public)
        self.users_public_client = self.identity_v2.UsersClient(**params_v2_public)

        # Clients below use the endpoint type of Keystone API v3, which is set
        # in endpoint_type
        params_v3 = {"endpoint_type": CONF.identity.v3_endpoint_type}
        self.domains_client = self.identity_v3.DomainsClient(**params_v3)
        self.identity_v3_client = self.identity_v3.IdentityClient(**params_v3)
        self.trusts_client = self.identity_v3.TrustsClient(**params_v3)
        self.users_v3_client = self.identity_v3.UsersClient(**params_v3)
        self.endpoints_v3_client = self.identity_v3.EndPointsClient(**params_v3)
        self.roles_v3_client = self.identity_v3.RolesClient(**params_v3)
        self.inherited_roles_client = self.identity_v3.InheritedRolesClient(**params_v3)
        self.role_assignments_client = self.identity_v3.RoleAssignmentsClient(
            **params_v3
        )
        self.identity_services_v3_client = self.identity_v3.ServicesClient(**params_v3)
        self.policies_client = self.identity_v3.PoliciesClient(**params_v3)
        self.projects_client = self.identity_v3.ProjectsClient(**params_v3)
        self.regions_client = self.identity_v3.RegionsClient(**params_v3)
        self.credentials_client = self.identity_v3.CredentialsClient(**params_v3)
        self.groups_client = self.identity_v3.GroupsClient(**params_v3)
        self.identity_versions_v3_client = self.identity_v3.VersionsClient(**params_v3)
        self.oauth_consumers_client = self.identity_v3.OAUTHConsumerClient(**params_v3)
        self.oauth_token_client = self.identity_v3.OAUTHTokenClient(**params_v3)
        self.domain_config_client = self.identity_v3.DomainConfigurationClient(
            **params_v3
        )
        self.endpoint_filter_client = self.identity_v3.EndPointsFilterClient(
            **params_v3
        )
        self.endpoint_groups_client = self.identity_v3.EndPointGroupsClient(**params_v3)
        self.catalog_client = self.identity_v3.CatalogClient(**params_v3)
        self.project_tags_client = self.identity_v3.ProjectTagsClient(**params_v3)
        self.application_credentials_client = (
            self.identity_v3.ApplicationCredentialsClient(**params_v3)
        )
        self.access_rules_client = self.identity_v3.AccessRulesClient(**params_v3)
        self.identity_limits_client = self.identity_v3.LimitsClient(**params_v3)
        if CONF.common.target_cloud == "flava":
            self.sa_credentials_client = self.identity_v3.SACredentialsClient(
                client=FlavaSACredentialsClient, **params_v3
            )

        # Token clients do not use the catalog. They only need default_params.
        # They read auth_url, so they should only be set if the corresponding
        # API version is marked as enabled
        if CONF.identity_feature_enabled.api_v2:
            if CONF.identity.uri:
                self.token_client = self.identity_v2.TokenClient(
                    auth_url=CONF.identity.uri
                )
            else:
                msg = "Identity v2 API enabled, but no identity.uri set"
                raise lib_exc.InvalidConfiguration(msg)
        if CONF.identity_feature_enabled.api_v3:
            if CONF.identity.uri_v3:
                self.token_v3_client = self.identity_v3.V3TokenClient(
                    auth_url=CONF.identity.uri_v3
                )
            else:
                msg = "Identity v3 API enabled, but no identity.uri_v3 set"
                raise lib_exc.InvalidConfiguration(msg)

    def _set_volume_clients(self):
        self.backups_client_latest = self.volume_v3.BackupsClient()
        self.encryption_types_client_latest = self.volume_v3.EncryptionTypesClient()
        self.snapshot_manage_client_latest = self.volume_v3.SnapshotManageClient()
        self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
        self.volume_capabilities_client_latest = self.volume_v3.CapabilitiesClient()
        self.volume_manage_client_latest = self.volume_v3.VolumeManageClient()
        self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
        self.volume_services_client_latest = self.volume_v3.ServicesClient()
        self.volume_types_client_latest = self.volume_v3.TypesClient()
        self.volume_hosts_client_latest = self.volume_v3.HostsClient()
        self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
        self.volume_quota_classes_client_latest = self.volume_v3.QuotaClassesClient()
        self.volume_scheduler_stats_client_latest = (
            self.volume_v3.SchedulerStatsClient()
        )
        self.volume_transfers_client_latest = self.volume_v3.TransfersClient()
        self.volume_transfers_mv355_client_latest = self.volume_v3.TransfersV355Client()
        self.volume_availability_zone_client_latest = (
            self.volume_v3.AvailabilityZoneClient()
        )
        self.volume_limits_client_latest = self.volume_v3.LimitsClient()
        self.volumes_client_latest = self.volume_v3.VolumesClient()
        self.volumes_extension_client_latest = self.volume_v3.ExtensionsClient()
        self.group_types_client_latest = self.volume_v3.GroupTypesClient()
        self.groups_client_latest = self.volume_v3.GroupsClient()
        self.group_snapshots_client_latest = self.volume_v3.GroupSnapshotsClient()
        self.volume_messages_client_latest = self.volume_v3.MessagesClient()
        self.volume_versions_client_latest = self.volume_v3.VersionsClient()
        self.attachments_client_latest = self.volume_v3.AttachmentsClient()

        # TODO(gmann): Below alias for service clients have been
        # deprecated and will be removed in future. Start using the alias
        # defined above with suffix _latest.
        # ****************Deprecated alias start from here***************
        self.backups_v2_client = self.volume_v3.BackupsClient()
        self.encryption_types_v2_client = self.volume_v3.EncryptionTypesClient()
        self.snapshot_manage_v2_client = self.volume_v3.SnapshotManageClient()
        self.snapshots_v2_client = self.volume_v3.SnapshotsClient()
        self.volume_capabilities_v2_client = self.volume_v3.CapabilitiesClient()
        self.volume_manage_v2_client = self.volume_v3.VolumeManageClient()
        self.volume_qos_v2_client = self.volume_v3.QosSpecsClient()
        self.volume_services_v2_client = self.volume_v3.ServicesClient()
        self.volume_types_v2_client = self.volume_v3.TypesClient()
        self.volume_hosts_v2_client = self.volume_v3.HostsClient()
        self.volume_quotas_v2_client = self.volume_v3.QuotasClient()
        self.volume_quota_classes_v2_client = self.volume_v3.QuotaClassesClient()
        self.volume_scheduler_stats_v2_client = self.volume_v3.SchedulerStatsClient()
        self.volume_transfers_v2_client = self.volume_v3.TransfersClient()
        self.volume_v2_availability_zone_client = (
            self.volume_v3.AvailabilityZoneClient()
        )
        self.volume_v2_limits_client = self.volume_v3.LimitsClient()
        self.volumes_v2_client = self.volume_v3.VolumesClient()
        self.volumes_v2_extension_client = self.volume_v3.ExtensionsClient()

        self.backups_v3_client = self.volume_v3.BackupsClient()
        self.group_types_v3_client = self.volume_v3.GroupTypesClient()
        self.groups_v3_client = self.volume_v3.GroupsClient()
        self.group_snapshots_v3_client = self.volume_v3.GroupSnapshotsClient()
        self.snapshots_v3_client = self.volume_v3.SnapshotsClient()
        self.volume_v3_messages_client = self.volume_v3.MessagesClient()
        self.volume_v3_versions_client = self.volume_v3.VersionsClient()
        self.volumes_v3_client = self.volume_v3.VolumesClient()
        # ****************Deprecated alias end here***********************

    def _set_object_storage_clients(self):
        self.account_client = self.object_storage.AccountClient()
        self.bulk_client = self.object_storage.BulkMiddlewareClient()
        self.capabilities_client = self.object_storage.CapabilitiesClient()
        self.container_client = self.object_storage.ContainerClient()
        self.object_client = self.object_storage.ObjectClient()

    # Add new LB service clients set methods
    def _set_lb_clients(self):
        # Set LB client depend on cloud service
        if CONF.common.target_cloud == "verda":
            self.loadbalancer_client = self.loadbalancer.LoadBalanceClient(
                client=VerdaLbClient()
            )
        elif CONF.common.target_cloud == "flava":
            self.loadbalancer_client = self.loadbalancer.LoadBalanceClient(
                client=FlavaLbClient()
            )

    # Add new VPC service clients set methods
    def _set_vpc_clients(self):
        # Set VPC client depend on cloud service
        if CONF.common.target_cloud == "verda":
            self.vpc_client = self.vpc.VpcClient(client=VerdaVpcClient())
        elif CONF.common.target_cloud == "flava":
            self.vpc_client = self.vpc.VpcClient(
                client=FlavaVpcClient(),
                build_timeout=CONF.vpc.build_timeout,
                build_interval=CONF.vpc.build_interval,
            )

    # Add new FaaS clients set methods
    def _set_faas_clients(self):
        # Set FaaS client depend on cloud service
        if CONF.common.target_cloud == "flava":
            self.faas_available_client = self.faas.AvailableClient(
                client=FlavaAvailableClient()
            )
            self.faas_functions_client = self.faas.FunctionsClient(
                client=FlavaFunctionsClient()
            )
            self.faas_templates_client = self.faas.TemplatesClient(
                client=FlavaTemplatesClient()
            )
            self.faas_github_user_client = self.faas.GithubUserClient(
                client=FlavaGithubUserClient(),
                employee_id=CONF.faas.employee_id,
            )
            self.faas_event_source_client = self.faas.EventSourceClient(
                client=FlavaEventSourceClient()
            )

    # Add new KaaS clients set methods
    def _set_kaas_clients(self):
        # Set KaaS client depend on cloud service
        if CONF.common.target_cloud == "flava":
            self.kaas_clusters_client = self.kaas.ClustersClient(
                client=FlavaClustersClient()
            )
            self.kaas_specs_client = self.kaas.SpecsClient(client=FlavaSpecsClient())

    # Add new EgressProxy clients set methods
    def _set_egress_proxy_clients(self):
        # Set EgressProxy client depend on cloud service
        if CONF.common.target_cloud == "flava":
            self.egress_proxy_client = self.egressproxy.EgressProxyClient(
                client=FlavaEgressProxyClient()
            )

    # Add new ContainerRegistry clients set methods
    def _set_container_registry_clients(self):
        # Set ContainerRegistry client depend on cloud service
        if CONF.common.target_cloud == "flava":
            self.container_registry_client = (
                self.container_registry.ContainerRegistryClient(
                    client=FlavaContainerRegistryClient()
                )
            )

    def _set_pulsar_clients(self):
        # Set Pulsar client depend on cloud service
        if CONF.common.target_cloud == "flava":
            self.pulsar_client = (
                self.flava_pulsar.PulsarClient(
                    client=FlavaPulsarClient()
                )
            )

    def _set_dns_clients(self):
        # Set DNS client depend on cloud service
        if CONF.common.target_cloud == "flava":
            self.dns_client = self.dns.DnsClient(
                client=FlavaDnsClient()
            )

    def _set_gslb_clients(self):
        # Set GSLB client depend on cloud service
        if CONF.common.target_cloud == "flava":
            self.gslb_client = self.gslb.GslbClient(
                client=FlavaGslbClient()
            )

    def _set_langfuse_clients(self):
        self.langfuse_projects_client = (
            self.langfuse.LangfuseProjectsClient(
                client=FlavaLangfuseProjectsClient(
                    auth_provider=self.auth_provider,
                    service='langfuse'
                )
            )
        )
        self.langfuse_admin_client = (
            self.langfuse.LangfuseAdminClient(
                client=FlavaLangfuseAdminClient(
                    auth_provider=self.auth_provider,
                    service='langfuse'
                )
            )
        )
        self.langfuse_available_client = (
            self.langfuse.LangfuseAvailableClient(
                client=FlavaLangfuseAvailableClient(
                    auth_provider=self.auth_provider,
                    service='langfuse'
                )
            )
        )

    def _set_servicemap_clients(self):
        """Set up ServiceMap clients (Flava only) using direct Athenz Bearer token auth."""
        if CONF.common.target_cloud == "flava":
            # Determine servicemap base URL based on environment
            test_env = os.environ.get("TEST_ENV", "")
            if "stage" in test_env:
                servicemap_base_url = "https://servicemap-stage-flava.linecorp.com"
            elif "real_dev" in test_env:
                servicemap_base_url = "https://flava-servicemap-dev-api.workers-hub.com"
            elif "real_prod" in test_env or "prod" in test_env:
                servicemap_base_url = "https://flava-servicemap-prod-api.workers-hub.com"
            else:
                raise lib_exc.EndpointNotFound(
                    f"ServiceMap endpoint not found for TEST_ENV: '{test_env}'. "
                    "Expected TEST_ENV to contain 'stage', 'real_dev', 'real_prod', or 'prod'."
                )

            # Create BearerTokenAuthProvider for servicemap (bypasses Keystone)
            servicemap_auth_provider = auth.BearerTokenAuthProvider(
                credentials=self.credentials,
                base_url=servicemap_base_url,
                token_attr='servicemap_access_token'
            )

            # Common client parameters
            client_params = {
                'auth_provider': servicemap_auth_provider,
                'service': 'servicemap',
                **self._bearer_client_params,
            }

            self.servicemap_project_client = ServicemapProjectClient(
                client=FlavaProjectClient(),
                **client_params
            )
            self.servicemap_service_client = ServicemapServiceClient(
                client=FlavaServiceClient(),
                **client_params
            )
            self.servicemap_service_group_client = ServicemapServiceGroupClient(
                client=FlavaServiceGroupClient(),
                **client_params
            )
            self.servicemap_service_resource_client = ServicemapServiceResourceClient(
                client=FlavaServiceResourceClient(),
                **client_params
            )
            self.servicemap_service_resource_group_client = ServicemapServiceResourceGroupClient(
                client=FlavaServiceResourceGroupClient(),
                **client_params
            )
            self.servicemap_service_relation_client = ServicemapServiceRelationClient(
                client=FlavaServiceRelationClient(),
                **client_params
            )

    def _set_redis_clients(self):
        """Set up Redis clients (Flava only) as non-catalog service using direct Bearer token auth."""
        if CONF.common.target_cloud == "flava":
            # Determine Redis base URL based on environment (following MySQL pattern)
            test_env = os.environ.get("TEST_ENV", "")
            if "stage" in test_env:
                if "kks" in test_env:
                    redis_base_url = "https://flava-stage-kks-redis.workers-hub-dev.com"
                elif "ssk" in test_env:
                    redis_base_url = "https://flava-stage-ssk-redis.workers-hub-dev.com"
                else:
                    redis_base_url = "https://flava-stage-redis.workers-hub-dev.com"
            elif "real_dev" in test_env:
                if "kks" in test_env:
                    redis_base_url = "https://flava-dev-kks-redis.workers-hub-dev.com"
                elif "ssk" in test_env:
                    redis_base_url = "https://flava-dev-ssk-redis.workers-hub-dev.com"
                else:
                    redis_base_url = "https://flava-dev-redis.workers-hub-dev.com"
            elif "real_prod" in test_env or "prod" in test_env:
                if "kks" in test_env:
                    redis_base_url = "https://flava-prod-kks-redis.workers-hub-dev.com"
                elif "ssk" in test_env:
                    redis_base_url = "https://flava-prod-ssk-redis.workers-hub-dev.com"
                else:
                    redis_base_url = "https://flava-prod-redis.workers-hub-dev.com"
            else:
                # Fallback for unknown environments - use stage kks
                redis_base_url = "https://flava-stage-kks-redis.workers-hub-dev.com"

            # Create BearerTokenAuthProvider for Redis (bypasses Keystone catalog)
            redis_auth_provider = auth.BearerTokenAuthProvider(
                credentials=self.credentials,
                base_url=redis_base_url,
                token_attr='redis_access_token'
            )

            # Common client parameters for Redis (without auth_provider to avoid conflicts)
            redis_client_params = {
                'service': 'redis',
                **self._bearer_client_params,
            }

            # Import Redis clients
            from lib.services.redis.available_client import AvailableClient
            from lib.services.redis.backup_client import BackupClient
            from lib.services.redis.dbrole_client import DBRoleClient
            from lib.services.redis.node_client import NodeClient
            from lib.services.redis.nodegroup_client import NodeGroupClient
            from lib.services.redis.parametergroup_client import ParameterGroupClient
            from lib.services.redis.quota_client import QuotaClient
            from lib.services.redis.service_client import ServiceClient
            from lib.services.redis.user_client import UserClient

            from lib.services.redis.flava_available_client import FlavaAvailableClient
            from lib.services.redis.flava_backup_client import FlavaBackupClient
            from lib.services.redis.flava_dbrole_client import FlavaDBRoleClient
            from lib.services.redis.flava_node_client import FlavaNodeClient
            from lib.services.redis.flava_nodegroup_client import FlavaNodeGroupClient
            from lib.services.redis.flava_parametergroup_client import FlavaParameterGroupClient
            from lib.services.redis.flava_quota_client import FlavaQuotaClient
            from lib.services.redis.flava_service_client import FlavaServiceClient
            from lib.services.redis.flava_user_client import FlavaUserClient

            # Direct instantiation (like ServiceMap) to avoid auth_provider conflicts
            self.redis_available_client = AvailableClient(
                auth_provider=redis_auth_provider,
                client=FlavaAvailableClient(),
                **redis_client_params
            )
            self.redis_backup_client = BackupClient(
                auth_provider=redis_auth_provider,
                client=FlavaBackupClient(),
                **redis_client_params
            )
            self.redis_dbrole_client = DBRoleClient(
                auth_provider=redis_auth_provider,
                client=FlavaDBRoleClient(),
                **redis_client_params
            )
            self.redis_node_client = NodeClient(
                auth_provider=redis_auth_provider,
                client=FlavaNodeClient(),
                **redis_client_params
            )
            self.redis_nodegroup_client = NodeGroupClient(
                auth_provider=redis_auth_provider,
                client=FlavaNodeGroupClient(),
                **redis_client_params
            )
            self.redis_parametergroup_client = ParameterGroupClient(
                auth_provider=redis_auth_provider,
                client=FlavaParameterGroupClient(),
                **redis_client_params
            )
            self.redis_quota_client = QuotaClient(
                auth_provider=redis_auth_provider,
                client=FlavaQuotaClient(),
                **redis_client_params
            )
            self.redis_service_client = ServiceClient(
                auth_provider=redis_auth_provider,
                client=FlavaServiceClient(),
                **redis_client_params
            )
            self.redis_user_client = UserClient(
                auth_provider=redis_auth_provider,
                client=FlavaUserClient(),
                **redis_client_params
            )

    def _set_api_gateway_clients(self):
        """Set up API Gateway clients (Flava only) using direct Athenz Bearer token auth."""
        if CONF.common.target_cloud == "flava":
            test_env = os.environ.get("TEST_ENV", "")
            if "stage" in test_env:
                api_gateway_base_url = "https://flapigw-provider-api.stg.flava.workers-infra.com"
            else:
                LOG.warning(
                    "No %s endpoint configured for TEST_ENV=%s; "
                    "%s clients not initialized", "api_gateway", test_env, "api_gateway"
                )
                return

            api_gateway_auth_provider = auth.BearerTokenAuthProvider(
                credentials=self.credentials,
                base_url=api_gateway_base_url,
                token_attr='api_gateway_access_token'
            )

            api_gateway_client_params = {
                'service': 'api_gateway',
                'region': CONF.identity.region,
            }

            self.api_gateway_apis_client = ApiGatewayApisClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaApisClient(),
                **api_gateway_client_params
            )
            self.api_gateway_stages_client = ApiGatewayStagesClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaStagesClient(),
                **api_gateway_client_params
            )
            self.api_gateway_paths_client = ApiGatewayPathsClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaPathsClient(),
                **api_gateway_client_params
            )
            self.api_gateway_methods_client = ApiGatewayMethodsClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaMethodsClient(),
                **api_gateway_client_params
            )
            self.api_gateway_keys_client = ApiGatewayKeysClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaKeysClient(),
                **api_gateway_client_params
            )
            self.api_gateway_keysets_client = ApiGatewayKeysetsClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaKeysetsClient(),
                **api_gateway_client_params
            )
            self.api_gateway_projects_client = ApiGatewayProjectsClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaProjectsClient(),
                **api_gateway_client_params
            )
            self.api_gateway_tags_client = ApiGatewayTagsClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaTagsClient(),
                **api_gateway_client_params
            )
            self.api_gateway_access_control_client = ApiGatewayAccessControlClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaAccessControlClient(),
                **api_gateway_client_params
            )
            self.api_gateway_monitoring_client = ApiGatewayMonitoringClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaMonitoringClient(),
                **api_gateway_client_params
            )
            self.api_gateway_staged_methods_client = ApiGatewayStagedMethodsClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaStagedMethodsClient(),
                **api_gateway_client_params
            )
            self.api_gateway_staged_paths_client = ApiGatewayStagedPathsClient(
                auth_provider=api_gateway_auth_provider,
                client=FlavaStagedPathsClient(),
                **api_gateway_client_params
            )

    def _set_rollouts_clients(self):
        """Set up Rollouts clients (Flava only) using direct Athenz Bearer token auth."""
        if CONF.common.target_cloud != "flava":
            return

        # Determine rollouts base URL based on environment
        test_env = os.environ.get("TEST_ENV", "")
        if "stage" in test_env:
            rollouts_base_url = "https://fe-api-flava-rollouts-sdkks.stg.flava.workers-infra.com"
        elif "dev" in test_env:
            rollouts_base_url = "https://fe-api-flava-rollouts-ddkks.workers-hub-dev.com"
        else:
            # No rollouts endpoint configured for this TEST_ENV (e.g. prod).
            # Skip client setup instead of failing Manager construction --
            # rollouts tests are env-gated via @env_only and will skip
            # themselves; unrelated tests don't need this client at all.
            LOG.warning(
                "No rollouts endpoint configured for TEST_ENV=%s; "
                "rollouts clients not initialized", test_env
            )
            return

        # Create BearerTokenAuthProvider for rollouts (bypasses Keystone)
        rollouts_auth_provider = auth.BearerTokenAuthProvider(
            credentials=self.credentials,
            base_url=rollouts_base_url,
            token_attr='rollouts_access_token'
        )

        # Common client parameters
        rollouts_client_params = {
            'service': 'rollouts',
            **self._bearer_client_params,
        }

        # Import rollouts clients
        from lib.services.rollouts.rollout_client import RolloutClient
        from lib.services.rollouts.status_client import StatusClient as RolloutsStatusClient
        from lib.services.rollouts.action_client import ActionClient as RolloutsActionClient
        from lib.services.rollouts.history_client import HistoryClient as RolloutsHistoryClient
        from lib.services.rollouts.flava_rollout_client import FlavaRolloutClient
        from lib.services.rollouts.flava_status_client import FlavaStatusClient
        from lib.services.rollouts.flava_action_client import FlavaActionClient
        from lib.services.rollouts.flava_history_client import FlavaHistoryClient

        self.rollouts_rollout_client = RolloutClient(
            auth_provider=rollouts_auth_provider,
            client=FlavaRolloutClient(),
            **rollouts_client_params
        )
        self.rollouts_status_client = RolloutsStatusClient(
            auth_provider=rollouts_auth_provider,
            client=FlavaStatusClient(),
            **rollouts_client_params
        )
        self.rollouts_action_client = RolloutsActionClient(
            auth_provider=rollouts_auth_provider,
            client=FlavaActionClient(),
            **rollouts_client_params
        )
        self.rollouts_history_client = RolloutsHistoryClient(
            auth_provider=rollouts_auth_provider,
            client=FlavaHistoryClient(),
            **rollouts_client_params
        )

    def _set_cloud_blueprint_clients(self):
        """Set up Cloud Blueprint clients (Flava only) using direct Athenz Bearer token auth."""
        if CONF.common.target_cloud == "flava":
            test_env = os.environ.get("TEST_ENV", "")
            if "stage" in test_env:
                cloud_blueprint_base_url = "https://flava-stage-cloud-blueprint.workers-hub-dev.com/"
            else:
                LOG.warning(
                    "No cloud_blueprint endpoint configured for TEST_ENV=%s; "
                    "cloud_blueprint clients not initialized", test_env
                )
                return

            cloud_blueprint_auth_provider = auth.BearerTokenAuthProvider(
                credentials=self.credentials,
                base_url=cloud_blueprint_base_url,
                token_attr='cloud_blueprint_access_token'
            )

            cloud_blueprint_client_params = {
                'service': 'cloud_blueprint',
                'region': CONF.identity.region,
            }

            self.cloud_blueprint_metadata_client = MetadataClient(
                auth_provider=cloud_blueprint_auth_provider,
                client=FlavaMetadataClient(),
                **cloud_blueprint_client_params
            )
            self.cloud_blueprint_projects_client = ProjectsClient(
                auth_provider=cloud_blueprint_auth_provider,
                client=FlavaBlueprintProjectsClient(),
                **cloud_blueprint_client_params
            )
            self.cloud_blueprint_deployments_client = DeploymentsClient(
                auth_provider=cloud_blueprint_auth_provider,
                client=FlavaDeploymentsClient(),
                **cloud_blueprint_client_params
            )
            self.cloud_blueprint_runs_client = RunsClient(
                auth_provider=cloud_blueprint_auth_provider,
                client=FlavaRunsClient(),
                **cloud_blueprint_client_params
            )
            self.cloud_blueprint_run_tasks_client = RunTasksClient(
                auth_provider=cloud_blueprint_auth_provider,
                client=FlavaRunTasksClient(),
                **cloud_blueprint_client_params
            )
            self.cloud_blueprint_configuration_versions_client = ConfigurationVersionsClient(
                auth_provider=cloud_blueprint_auth_provider,
                client=FlavaConfigurationVersionsClient(),
                **cloud_blueprint_client_params
            )
            self.cloud_blueprint_github_client = GithubClient(
                auth_provider=cloud_blueprint_auth_provider,
                client=FlavaGithubClient(),
                **cloud_blueprint_client_params
            )


def get_auth_provider_class(credentials):
    if isinstance(credentials, auth.KeystoneV3Credentials):
        return auth.KeystoneV3AuthProvider, CONF.identity.uri_v3
    else:
        return auth.KeystoneV2AuthProvider, CONF.identity.uri


def get_auth_provider(credentials, pre_auth=False, scope="project"):
    # kwargs for auth provider match the common ones used by service clients
    default_params = config.service_client_config()
    if credentials is None:
        raise lib_exc.InvalidCredentials("Credentials must be specified")
    auth_provider_class, auth_url = get_auth_provider_class(credentials)
    _auth_provider = auth_provider_class(
        credentials, auth_url, scope=scope, **default_params
    )
    if pre_auth:
        _auth_provider.set_auth()
    return _auth_provider
