# Copyright 2018 Red Hat, Inc.
#
# 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.

import importlib
import warnings

import os_service_types

from openstack import _log
from openstack import service_description

_logger = _log.setup_logging('openstack')
_service_type_manager = os_service_types.ServiceTypes()


def make_names():
    imports = ['from openstack import service_description']
    services = []

    for service in _service_type_manager.services:
        service_type = service['service_type']
        if service_type == 'ec2-api':
            # NOTE(mordred) It doesn't make any sense to use ec2-api
            # from openstacksdk. The credentials API calls are all calls
            # on identity endpoints.
            continue
        desc_class = _find_service_description_class(service_type)

        st = service_type.replace('-', '_')

        if desc_class.__module__ != 'openstack.service_description':
            base_mod, dm = desc_class.__module__.rsplit('.', 1)
            imports.append(f'from {base_mod} import {dm}')
        else:
            dm = 'service_description'

        dc = desc_class.__name__
        services.append(
            f"{st} = {dm}.{dc}(service_type='{service_type}')",
        )

        # Register the descriptor class with every known alias. Don't
        # add doc strings though - although they are supported, we don't
        # want to give anybody any bad ideas. Making a second descriptor
        # does not introduce runtime cost as the descriptors all use
        # the same _proxies dict on the instance.
        for alias_name in _get_aliases(st):
            if alias_name[-1].isdigit():
                continue
            services.append(f'{alias_name} = {st}')
        services.append('')
    print("# Generated file, to change, run tools/print-services.py")
    for imp in sorted(imports):
        print(imp)
    print('\n')
    print("class ServicesMixin:\n")
    for service in services:
        if service:
            print(f"    {service}")
        else:
            print()


def _get_aliases(service_type, aliases=None):
    # We make connection attributes for all official real type names
    # and aliases. Three services have names they were called by in
    # openstacksdk that are not covered by Service Types Authority aliases.
    # Include them here - but take heed, no additional values should ever
    # be added to this list.
    # that were only used in openstacksdk resource naming.
    LOCAL_ALIASES = {
        'baremetal': 'bare_metal',
        'block_storage': 'block_store',
        'clustering': 'cluster',
    }
    all_types = set(_service_type_manager.get_aliases(service_type))
    if aliases:
        all_types.update(aliases)
    if service_type in LOCAL_ALIASES:
        all_types.add(LOCAL_ALIASES[service_type])
    all_aliases = set()
    for alias in all_types:
        all_aliases.add(alias.replace('-', '_'))
    return all_aliases


def _find_service_description_class(service_type):
    package_name = f'openstack.{service_type}'.replace('-', '_')
    module_name = service_type.replace('-', '_') + '_service'
    class_name = ''.join(
        [part.capitalize() for part in module_name.split('_')]
    )

    # We have some exceptions :(
    # This should have been called 'shared-filesystem'
    if service_type == 'shared-file-system':
        class_name = 'SharedFilesystemService'

    try:
        import_name = '.'.join([package_name, module_name])
        service_description_module = importlib.import_module(import_name)
    except ImportError as e:
        # ImportWarning is ignored by default. This warning is here
        # as an opt-in for people trying to figure out why something
        # didn't work.
        warnings.warn(
            f"Could not import {service_type} service description: {str(e)}",
            ImportWarning,
        )
        return service_description.ServiceDescription

    # There are no cases in which we should have a module but not the class
    # inside it.
    service_description_class = getattr(service_description_module, class_name)
    return service_description_class


make_names()
