#!/usr/bin/python
# Copyright (c) 2017, Daniel Korn <korndaniel1@gmail.com>
# Copyright (c) 2017, Yaacov Zamir <yzamir@redhat.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import annotations

DOCUMENTATION = r"""
module: manageiq_provider
short_description: Management of provider in ManageIQ
extends_documentation_fragment:
  - community.general._manageiq
  - community.general._attributes

author: Daniel Korn (@dkorn)
description:
  - The manageiq_provider module supports adding, updating, and deleting provider in ManageIQ.
attributes:
  check_mode:
    support: none
  diff_mode:
    support: none

options:
  state:
    type: str
    description:
      - V(absent) - provider should not exist,
      - V(present) - provider should be present,
      - V(refresh) - provider is refreshed.
    choices: ['absent', 'present', 'refresh']
    default: 'present'
  name:
    type: str
    description: The provider's name.
    required: true
  type:
    type: str
    description: The provider's type.
    choices: ['Openshift', 'Amazon', 'oVirt', 'VMware', 'Azure', 'Director', 'OpenStack', 'GCE']
  zone:
    type: str
    description: The ManageIQ zone name that manages the provider.
    default: 'default'
  provider_region:
    type: str
    description: The provider region name to connect to (for example AWS region for Amazon).
  host_default_vnc_port_start:
    type: str
    description: The first port in the host VNC range.
  host_default_vnc_port_end:
    type: str
    description: The last port in the host VNC range.
  subscription:
    type: str
    description: Microsoft Azure subscription ID.
  project:
    type: str
    description: Google Compute Engine Project ID.
  azure_tenant_id:
    type: str
    description: Tenant ID. Defaults to V(null).
    aliases: [keystone_v3_domain_id]
  tenant_mapping_enabled:
    type: bool
    default: false
    description: Whether to enable mapping of existing tenants.
  api_version:
    type: str
    description: The OpenStack Keystone API version.
    choices: ['v2', 'v3']

  provider:
    description: Default endpoint connection information, required if state is true.
    type: dict
    suboptions:
      hostname:
        type: str
        description: The provider's API hostname.
        required: true
      port:
        type: int
        description: The provider's API port.
      userid:
        type: str
        description: Provider's API endpoint authentication userid.
      password:
        type: str
        description: Provider's API endpoint authentication password.
      auth_key:
        type: str
        description: Provider's API endpoint authentication bearer token.
      validate_certs:
        description: Whether SSL certificates should be verified for HTTPS requests (deprecated).
        type: bool
        default: true
        aliases: [verify_ssl]
      security_protocol:
        type: str
        description: How SSL certificates should be used for HTTPS requests.
        choices: ['ssl-with-validation', 'ssl-with-validation-custom-ca', 'ssl-without-validation', 'non-ssl']
      certificate_authority:
        type: str
        description: The CA bundle string with custom certificates.
      path:
        type: str
        description:
          - TODO needs documentation.
      project:
        type: str
        description:
          - TODO needs documentation.
      role:
        type: str
        description:
          - TODO needs documentation.
      subscription:
        type: str
        description:
          - TODO needs documentation.
      uid_ems:
        type: str
        description:
          - TODO needs documentation.
  metrics:
    description: Metrics endpoint connection information.
    type: dict
    suboptions:
      hostname:
        type: str
        description: The provider's API hostname.
        required: true
      port:
        type: int
        description: The provider's API port.
      userid:
        type: str
        description: Provider's API endpoint authentication userid.
      password:
        type: str
        description: Provider's API endpoint authentication password.
      auth_key:
        type: str
        description: Provider's API endpoint authentication bearer token.
      validate_certs:
        description: Whether SSL certificates should be verified for HTTPS requests (deprecated).
        type: bool
        default: true
        aliases: [verify_ssl]
      security_protocol:
        type: str
        choices: ['ssl-with-validation', 'ssl-with-validation-custom-ca', 'ssl-without-validation', 'non-ssl']
        description: How SSL certificates should be used for HTTPS requests.
      certificate_authority:
        type: str
        description: The CA bundle string with custom certificates.
      path:
        type: str
        description: Database name for oVirt metrics. Defaults to V(ovirt_engine_history).
      project:
        type: str
        description:
          - TODO needs documentation.
      role:
        type: str
        description:
          - TODO needs documentation.
      subscription:
        type: str
        description:
          - TODO needs documentation.
      uid_ems:
        type: str
        description:
          - TODO needs documentation.
  alerts:
    description: Alerts endpoint connection information.
    type: dict
    suboptions:
      hostname:
        type: str
        description: The provider's API hostname.
        required: true
      port:
        type: int
        description: The provider's API port.
      userid:
        type: str
        description: Provider's API endpoint authentication userid. Defaults to V(null).
      password:
        type: str
        description: Provider's API endpoint authentication password. Defaults to V(null).
      auth_key:
        type: str
        description: Provider's API endpoint authentication bearer token. Defaults to V(null).
      validate_certs:
        type: bool
        description: Whether SSL certificates should be verified for HTTPS requests (deprecated). Defaults to V(true).
        default: true
        aliases: [verify_ssl]
      security_protocol:
        type: str
        choices: ['ssl-with-validation', 'ssl-with-validation-custom-ca', 'ssl-without-validation', 'non-ssl']
        description: How SSL certificates should be used for HTTPS requests. Defaults to V(null).
      certificate_authority:
        type: str
        description: The CA bundle string with custom certificates. Defaults to V(null).
      path:
        type: str
        description:
          - TODO needs documentation.
      project:
        type: str
        description:
          - TODO needs documentation.
      role:
        type: str
        description:
          - TODO needs documentation.
      subscription:
        type: str
        description:
          - TODO needs documentation.
      uid_ems:
        type: str
        description:
          - TODO needs documentation.
  ssh_keypair:
    description: SSH key pair used for SSH connections to all hosts in this provider.
    type: dict
    suboptions:
      hostname:
        type: str
        description: Director hostname.
        required: true
      userid:
        type: str
        description: SSH username.
      auth_key:
        type: str
        description: SSH private key.
      validate_certs:
        description:
          - Whether certificates should be verified for connections.
        type: bool
        default: true
        aliases: [verify_ssl]
      security_protocol:
        type: str
        choices: ['ssl-with-validation', 'ssl-with-validation-custom-ca', 'ssl-without-validation', 'non-ssl']
        description:
          - TODO needs documentation.
      certificate_authority:
        type: str
        description:
          - TODO needs documentation.
      password:
        type: str
        description:
          - TODO needs documentation.
      path:
        type: str
        description:
          - TODO needs documentation.
      project:
        type: str
        description:
          - TODO needs documentation.
      role:
        type: str
        description:
          - TODO needs documentation.
      subscription:
        type: str
        description:
          - TODO needs documentation.
      uid_ems:
        type: str
        description:
          - TODO needs documentation.
      port:
        type: int
        description:
          - TODO needs documentation.
"""

EXAMPLES = r"""
- name: Create a new provider in ManageIQ ('Hawkular' metrics)
  community.general.manageiq_provider:
    name: 'EngLab'
    type: 'OpenShift'
    state: 'present'
    provider:
      auth_key: 'topSecret'
      hostname: 'example.com'
      port: 8443
      validate_certs: true
      security_protocol: 'ssl-with-validation-custom-ca'
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    metrics:
      auth_key: 'topSecret'
      role: 'hawkular'
      hostname: 'example.com'
      port: 443
      validate_certs: true
      security_protocol: 'ssl-with-validation-custom-ca'
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    manageiq_connection:
      url: 'https://127.0.0.1:80'
      username: 'admin'
      password: 'password'
      validate_certs: true


- name: Update an existing provider named 'EngLab' (defaults to 'Prometheus' metrics)
  community.general.manageiq_provider:
    name: 'EngLab'
    type: 'Openshift'
    state: 'present'
    provider:
      auth_key: 'topSecret'
      hostname: 'next.example.com'
      port: 8443
      validate_certs: true
      security_protocol: 'ssl-with-validation-custom-ca'
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    metrics:
      auth_key: 'topSecret'
      hostname: 'next.example.com'
      port: 443
      validate_certs: true
      security_protocol: 'ssl-with-validation-custom-ca'
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    manageiq_connection:
      url: 'https://127.0.0.1'
      username: 'admin'
      password: 'password'
      validate_certs: true


- name: Delete a provider in ManageIQ
  community.general.manageiq_provider:
    name: 'EngLab'
    type: 'Openshift'
    state: 'absent'
    manageiq_connection:
      url: 'https://127.0.0.1'
      username: 'admin'
      password: 'password'
      validate_certs: true


- name: Create a new Amazon provider in ManageIQ using token authentication
  community.general.manageiq_provider:
    name: 'EngAmazon'
    type: 'Amazon'
    state: 'present'
    provider:
      hostname: 'amazon.example.com'
      userid: 'hello'
      password: 'world'
    manageiq_connection:
      url: 'https://127.0.0.1'
      token: 'VeryLongToken'
      validate_certs: true


- name: Create a new oVirt provider in ManageIQ
  community.general.manageiq_provider:
    name: 'RHEV'
    type: 'oVirt'
    state: 'present'
    provider:
      hostname: 'rhev01.example.com'
      userid: 'admin@internal'
      password: 'password'
      validate_certs: true
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    metrics:
      hostname: 'metrics.example.com'
      path: 'ovirt_engine_history'
      userid: 'user_id_metrics'
      password: 'password_metrics'
      validate_certs: true
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    manageiq_connection:
      url: 'https://127.0.0.1'
      username: 'admin'
      password: 'password'
      validate_certs: true

- name: Create a new VMware provider in ManageIQ
  community.general.manageiq_provider:
    name: 'EngVMware'
    type: 'VMware'
    state: 'present'
    provider:
      hostname: 'vcenter.example.com'
      host_default_vnc_port_start: 5800
      host_default_vnc_port_end: 5801
      userid: 'root'
      password: 'password'
    manageiq_connection:
      url: 'https://127.0.0.1'
      token: 'VeryLongToken'
      validate_certs: true

- name: Create a new Azure provider in ManageIQ
  community.general.manageiq_provider:
    name: 'EngAzure'
    type: 'Azure'
    provider_region: 'northeurope'
    subscription: 'e272bd74-f661-484f-b223-88dd128a4049'
    azure_tenant_id: 'e272bd74-f661-484f-b223-88dd128a4048'
    state: 'present'
    provider:
      hostname: 'azure.example.com'
      userid: 'e272bd74-f661-484f-b223-88dd128a4049'
      password: 'password'
    manageiq_connection:
      url: 'https://cf-6af0.rhpds.opentlc.com'
      username: 'admin'
      password: 'password'
      validate_certs: true

- name: Create a new OpenStack Director provider in ManageIQ with rsa keypair
  community.general.manageiq_provider:
    name: 'EngDirector'
    type: 'Director'
    api_version: 'v3'
    state: 'present'
    provider:
      hostname: 'director.example.com'
      userid: 'admin'
      password: 'password'
      security_protocol: 'ssl-with-validation'
      validate_certs: 'true'
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    ssh_keypair:
      hostname: director.example.com
      userid: heat-admin
      auth_key: 'SecretSSHPrivateKey'

- name: Create a new OpenStack provider in ManageIQ with amqp metrics
  community.general.manageiq_provider:
    name: 'EngOpenStack'
    type: 'OpenStack'
    api_version: 'v3'
    state: 'present'
    provider_region: 'europe'
    tenant_mapping_enabled: 'False'
    keystone_v3_domain_id: 'mydomain'
    provider:
      hostname: 'openstack.example.com'
      userid: 'admin'
      password: 'password'
      security_protocol: 'ssl-with-validation'
      validate_certs: 'true'
      certificate_authority: |
        -----BEGIN CERTIFICATE-----
        FAKECERTsdKgAwI...
        -----END CERTIFICATE-----
    metrics:
      role: amqp
      hostname: 'amqp.example.com'
      security_protocol: 'non-ssl'
      port: 5666
      userid: admin
      password: password


- name: Create a new GCE provider in ManageIQ
  community.general.manageiq_provider:
    name: 'EngGoogle'
    type: 'GCE'
    provider_region: 'europe-west1'
    project: 'project1'
    state: 'present'
    provider:
      hostname: 'gce.example.com'
      auth_key: 'google_json_key'
      validate_certs: 'false'
"""

RETURN = r"""
"""

from ansible.module_utils.basic import AnsibleModule

from ansible_collections.community.general.plugins.module_utils._manageiq import ManageIQ, manageiq_argument_spec


def supported_providers():
    return dict(
        Openshift=dict(
            class_name="ManageIQ::Providers::Openshift::ContainerManager",
            authtype="bearer",
            default_role="default",
            metrics_role="prometheus",
            alerts_role="prometheus_alerts",
        ),
        Amazon=dict(
            class_name="ManageIQ::Providers::Amazon::CloudManager",
        ),
        oVirt=dict(
            class_name="ManageIQ::Providers::Redhat::InfraManager",
            default_role="default",
            metrics_role="metrics",
        ),
        VMware=dict(
            class_name="ManageIQ::Providers::Vmware::InfraManager",
        ),
        Azure=dict(
            class_name="ManageIQ::Providers::Azure::CloudManager",
        ),
        Director=dict(class_name="ManageIQ::Providers::Openstack::InfraManager", ssh_keypair_role="ssh_keypair"),
        OpenStack=dict(
            class_name="ManageIQ::Providers::Openstack::CloudManager",
        ),
        GCE=dict(
            class_name="ManageIQ::Providers::Google::CloudManager",
        ),
    )


def endpoint_list_spec():
    return dict(
        provider=dict(type="dict", options=endpoint_argument_spec()),
        metrics=dict(type="dict", options=endpoint_argument_spec()),
        alerts=dict(type="dict", options=endpoint_argument_spec()),
        ssh_keypair=dict(type="dict", options=endpoint_argument_spec(), no_log=False),
    )


def endpoint_argument_spec():
    return dict(
        role=dict(),
        hostname=dict(required=True),
        port=dict(type="int"),
        validate_certs=dict(default=True, type="bool", aliases=["verify_ssl"]),
        certificate_authority=dict(),
        security_protocol=dict(
            choices=[
                "ssl-with-validation",
                "ssl-with-validation-custom-ca",
                "ssl-without-validation",
                "non-ssl",
            ],
        ),
        userid=dict(),
        password=dict(no_log=True),
        auth_key=dict(no_log=True),
        subscription=dict(no_log=True),
        project=dict(),
        uid_ems=dict(),
        path=dict(),
    )


def delete_nulls(h):
    """Remove null entries from a hash

    Returns:
        a hash without nulls
    """
    if isinstance(h, list):
        return [delete_nulls(i) for i in h]
    if isinstance(h, dict):
        return {k: delete_nulls(v) for k, v in h.items() if v is not None}

    return h


class ManageIQProvider:
    """
    Object to execute provider management operations in manageiq.
    """

    def __init__(self, manageiq):
        self.manageiq = manageiq

        self.module = self.manageiq.module
        self.api_url = self.manageiq.api_url
        self.client = self.manageiq.client

    def class_name_to_type(self, class_name):
        """Convert class_name to type

        Returns:
            the type
        """
        out = [k for k, v in supported_providers().items() if v["class_name"] == class_name]
        if len(out) == 1:
            return out[0]

        return None

    def zone_id(self, name):
        """Search for zone id by zone name.

        Returns:
            the zone id, or send a module Fail signal if zone not found.
        """
        zone = self.manageiq.find_collection_resource_by("zones", name=name)
        if not zone:  # zone doesn't exist
            self.module.fail_json(msg=f"zone {name} does not exist in manageiq")

        return zone["id"]

    def provider(self, name):
        """Search for provider object by name.

        Returns:
            the provider, or None if provider not found.
        """
        return self.manageiq.find_collection_resource_by("providers", name=name)

    def build_connection_configurations(self, provider_type, endpoints):
        """Build "connection_configurations" objects from
        requested endpoints provided by user

        Returns:
            the user requested provider endpoints list
        """
        connection_configurations = []
        endpoint_keys = endpoint_list_spec().keys()
        provider_defaults = supported_providers().get(provider_type, {})

        # get endpoint defaults
        endpoint = endpoints.get("provider")
        default_auth_key = endpoint.get("auth_key")

        # build a connection_configuration object for each endpoint
        for endpoint_key in endpoint_keys:
            endpoint = endpoints.get(endpoint_key)
            if endpoint:
                # get role and authtype
                role = endpoint.get("role") or provider_defaults.get(f"{endpoint_key}_role", "default")
                if role == "default":
                    authtype = provider_defaults.get("authtype") or role
                else:
                    authtype = role

                # set a connection_configuration
                connection_configurations.append(
                    {
                        "endpoint": {
                            "role": role,
                            "hostname": endpoint.get("hostname"),
                            "port": endpoint.get("port"),
                            "verify_ssl": [0, 1][endpoint.get("validate_certs", True)],
                            "security_protocol": endpoint.get("security_protocol"),
                            "certificate_authority": endpoint.get("certificate_authority"),
                            "path": endpoint.get("path"),
                        },
                        "authentication": {
                            "authtype": authtype,
                            "userid": endpoint.get("userid"),
                            "password": endpoint.get("password"),
                            "auth_key": endpoint.get("auth_key") or default_auth_key,
                        },
                    }
                )

        return connection_configurations

    def delete_provider(self, provider):
        """Deletes a provider from manageiq.

        Returns:
            a short message describing the operation executed.
        """
        try:
            url = f"{self.api_url}/providers/{provider['id']}"
            result = self.client.post(url, action="delete")
        except Exception as e:
            self.module.fail_json(msg=f"failed to delete provider {provider['name']}: {e}")

        return dict(changed=True, msg=result["message"])

    def edit_provider(
        self,
        provider,
        name,
        provider_type,
        endpoints,
        zone_id,
        provider_region,
        host_default_vnc_port_start,
        host_default_vnc_port_end,
        subscription,
        project,
        uid_ems,
        tenant_mapping_enabled,
        api_version,
    ):
        """Edit a provider from manageiq.

        Returns:
            a short message describing the operation executed.
        """
        url = f"{self.api_url}/providers/{provider['id']}"

        resource = dict(
            name=name,
            zone={"id": zone_id},
            provider_region=provider_region,
            connection_configurations=endpoints,
            host_default_vnc_port_start=host_default_vnc_port_start,
            host_default_vnc_port_end=host_default_vnc_port_end,
            subscription=subscription,
            project=project,
            uid_ems=uid_ems,
            tenant_mapping_enabled=tenant_mapping_enabled,
            api_version=api_version,
        )

        # NOTE: we do not check for diff's between requested and current
        #       provider, we always submit endpoints with password or auth_keys,
        #       since we can not compare with current password or auth_key,
        #       every edit request is sent to ManageIQ API without comparing
        #       it to current state.

        # clean nulls, we do not send nulls to the api
        resource = delete_nulls(resource)

        # try to update provider
        try:
            result = self.client.post(url, action="edit", resource=resource)
        except Exception as e:
            self.module.fail_json(msg=f"failed to update provider {provider['name']}: {e}")

        return dict(changed=True, msg=f"successfully updated the provider {provider['name']}: {result}")

    def create_provider(
        self,
        name,
        provider_type,
        endpoints,
        zone_id,
        provider_region,
        host_default_vnc_port_start,
        host_default_vnc_port_end,
        subscription,
        project,
        uid_ems,
        tenant_mapping_enabled,
        api_version,
    ):
        """Creates the provider in manageiq.

        Returns:
            a short message describing the operation executed.
        """
        resource = dict(
            name=name,
            zone={"id": zone_id},
            provider_region=provider_region,
            host_default_vnc_port_start=host_default_vnc_port_start,
            host_default_vnc_port_end=host_default_vnc_port_end,
            subscription=subscription,
            project=project,
            uid_ems=uid_ems,
            tenant_mapping_enabled=tenant_mapping_enabled,
            api_version=api_version,
            connection_configurations=endpoints,
        )

        # clean nulls, we do not send nulls to the api
        resource = delete_nulls(resource)

        # try to create a new provider
        try:
            url = f"{self.api_url}/providers"
            result = self.client.post(url, type=supported_providers()[provider_type]["class_name"], **resource)
        except Exception as e:
            self.module.fail_json(msg=f"failed to create provider {name}: {e}")

        return dict(changed=True, msg=f"successfully created the provider {name}: {result['results']}")

    def refresh(self, provider, name):
        """Trigger provider refresh.

        Returns:
            a short message describing the operation executed.
        """
        try:
            url = f"{self.api_url}/providers/{provider['id']}"
            self.client.post(url, action="refresh")
        except Exception as e:
            self.module.fail_json(msg=f"failed to refresh provider {name}: {e}")

        return dict(changed=True, msg=f"refreshing provider {name}")


def main():
    zone_id = None
    endpoints = []
    argument_spec = dict(
        state=dict(choices=["absent", "present", "refresh"], default="present"),
        name=dict(required=True),
        zone=dict(default="default"),
        provider_region=dict(),
        host_default_vnc_port_start=dict(),
        host_default_vnc_port_end=dict(),
        subscription=dict(),
        project=dict(),
        azure_tenant_id=dict(aliases=["keystone_v3_domain_id"]),
        tenant_mapping_enabled=dict(default=False, type="bool"),
        api_version=dict(choices=["v2", "v3"]),
        type=dict(choices=list(supported_providers().keys())),
    )
    # add the manageiq connection arguments to the arguments
    argument_spec.update(manageiq_argument_spec())
    # add the endpoint arguments to the arguments
    argument_spec.update(endpoint_list_spec())

    module = AnsibleModule(
        argument_spec=argument_spec,
        required_if=[("state", "present", ["provider"]), ("state", "refresh", ["name"])],
        required_together=[["host_default_vnc_port_start", "host_default_vnc_port_end"]],
    )

    name = module.params["name"]
    zone_name = module.params["zone"]
    provider_type = module.params["type"]
    raw_endpoints = module.params
    provider_region = module.params["provider_region"]
    host_default_vnc_port_start = module.params["host_default_vnc_port_start"]
    host_default_vnc_port_end = module.params["host_default_vnc_port_end"]
    subscription = module.params["subscription"]
    uid_ems = module.params["azure_tenant_id"]
    project = module.params["project"]
    tenant_mapping_enabled = module.params["tenant_mapping_enabled"]
    api_version = module.params["api_version"]
    state = module.params["state"]

    manageiq = ManageIQ(module)
    manageiq_provider = ManageIQProvider(manageiq)

    provider = manageiq_provider.provider(name)

    # provider should not exist
    if state == "absent":
        # if we have a provider, delete it
        if provider:
            res_args = manageiq_provider.delete_provider(provider)
        # if we do not have a provider, nothing to do
        else:
            res_args = dict(changed=False, msg=f"provider {name}: does not exist in manageiq")

    # provider should exist
    if state == "present":
        # get data user did not explicitly give
        if zone_name:
            zone_id = manageiq_provider.zone_id(zone_name)

        # if we do not have a provider_type, use the current provider_type
        if provider and not provider_type:
            provider_type = manageiq_provider.class_name_to_type(provider["type"])

        # check supported_providers types
        if not provider_type:
            manageiq_provider.module.fail_json(msg="missing required argument: provider_type")

        # check supported_providers types
        if provider_type not in supported_providers().keys():
            manageiq_provider.module.fail_json(msg=f"provider_type {provider_type} is not supported")

        # build "connection_configurations" objects from user requested endpoints
        # "provider" is a required endpoint, if we have it, we have endpoints
        if raw_endpoints.get("provider"):
            endpoints = manageiq_provider.build_connection_configurations(provider_type, raw_endpoints)

        # if we have a provider, edit it
        if provider:
            res_args = manageiq_provider.edit_provider(
                provider,
                name,
                provider_type,
                endpoints,
                zone_id,
                provider_region,
                host_default_vnc_port_start,
                host_default_vnc_port_end,
                subscription,
                project,
                uid_ems,
                tenant_mapping_enabled,
                api_version,
            )
        # if we do not have a provider, create it
        else:
            res_args = manageiq_provider.create_provider(
                name,
                provider_type,
                endpoints,
                zone_id,
                provider_region,
                host_default_vnc_port_start,
                host_default_vnc_port_end,
                subscription,
                project,
                uid_ems,
                tenant_mapping_enabled,
                api_version,
            )

    # refresh provider (trigger sync)
    if state == "refresh":
        if provider:
            res_args = manageiq_provider.refresh(provider, name)
        else:
            res_args = dict(changed=False, msg=f"provider {name}: does not exist in manageiq")

    module.exit_json(**res_args)


if __name__ == "__main__":
    main()
