#!/usr/bin/env python
"""
Main entry point for a click based CLI.
"""
import functools
import json
import logging
import os
import sys

import click
from firestone_lib import cli
from firestone_lib import utils as firestone_utils

from {{ client_pkg }} import api_client
from {{ client_pkg }} import configuration
from {{ client_pkg }} import exceptions
{% for rsrc in rsrcs -%}

{% set ns = namespace(has_create=False, has_update=False) -%}
{% for op in rsrc["operations"]["resource"] -%}
{% if op["name"] == "create" -%}
{% set ns.has_create = True -%}
{% break -%}
{% endif -%}
{% endfor -%}

{% for op in rsrc["operations"]["instance"] -%}
{% if op["name"] == "update" -%}
{% set ns.has_update = True -%}
{% break -%}
{% endif -%}
{% endfor -%}

{% set comp_name = rsrc["name"] if not rsrc["name"].endswith("s") else rsrc["name"][:-1] %}
from {{ client_pkg }}.api import {{ rsrc["name"] }}_api
from {{ client_pkg }}.models import {{ comp_name }} as {{ comp_name }}_model
{% if ns.has_create -%}
from {{ client_pkg }}.models import create_{{ comp_name }} as create_{{ comp_name }}_model
{% endif -%}
{% if ns.has_update -%}
from {{ client_pkg }}.models import update_{{ comp_name }} as update_{{ comp_name }}_model
{% endif -%}
{% endfor %}

_LOGGER = logging.getLogger(__name__)


def api_exc(func):
    """Handle ApiExceptions in all functions."""
    async def wrapper(*args, **kwargs):
        resp = None
        try:
            return await func(*args, **kwargs)
        except exceptions.ApiException as apie:
            if apie.body:
                click.echo(apie.body)
            else:
                click.echo(apie.reason)

            api_obj = args[0].get("api_obj")
            if api_obj:
                await api_obj.api_client.close()
        sys.exit(-1)

    return functools.update_wrapper(wrapper, func)


@click.group()
@click.option("--debug", help="Turn on debugging", is_flag=True)
@click.option(
    "--api-key",
    help="The API key to authorize against API",
    envvar="API_KEY",
)
@click.option(
    "--api-url",
    help="The API url, e.g. https://localhost",
    envvar="API_URL",
)
@click.option(
    "--client-cert",
    help="Path to the client cert for mutual TLS",
    envvar="CLIENT_CERT",
)
@click.option(
    "--client-key",
    help="Path to the client key for mutual TLS",
    envvar="CLIENT_KEY",
)
@click.option("--trust-proxy", help="Trust the proxy env vars", is_flag=True, default=False)
@click.pass_context
def main(ctx, debug, api_key, api_url, client_cert, client_key, trust_proxy):
    """{{ title }}

    {{ description }}
    """
    if not trust_proxy:
        for prefix in ["http", "https", "all_http", "all_https"]:
            env_var = f"{prefix}_proxy"
            if env_var in os.environ:
                del os.environ[env_var]
            if env_var.upper() in os.environ:
                del os.environ[env_var.upper()]

    try:
        cli.init_logging("{{ pkg }}.resources.logging", "cli.conf")
    # pylint: disable=broad-except
    except Exception:
        logging.basicConfig(
            level=logging.INFO,
            format="# %(asctime)s - [%(threadName)s] %(name)s:%(lineno)d %(levelname)s - %(message)s",
        )

    logging.getLogger("asyncio").setLevel(logging.CRITICAL)
    if debug:
        _LOGGER.setLevel(logging.DEBUG)
        logging.getLogger().setLevel(logging.DEBUG)
        logging.getLogger("{{ pkg }}").setLevel(logging.DEBUG)
        logging.getLogger("aiohttp").setLevel(logging.DEBUG)
        logging.getLogger("urllib3").setLevel(logging.DEBUG)
        logging.getLogger("httplib").setLevel(logging.DEBUG)

    config = configuration.Configuration(host=api_url)
    config.debug = debug
    if api_key:
        config.access_token = api_key
    if client_cert:
        config.cert_file = client_cert
    if client_key:
        config.key_file = client_key
    if "SSL_CA_CERT" in os.environ:
        config.ssl_ca_cert = os.environ["SSL_CA_CERT"]
    if "REQUESTS_CA_BUNDLE" in os.environ:
        config.ssl_ca_cert = os.environ["REQUESTS_CA_BUNDLE"]

    aclient = api_client.ApiClient(configuration=config)

    ctx.obj = {
        "api_client": aclient,
    }
    _LOGGER.debug(f"ctx.obj: {ctx.obj}")


{% for rsrc in rsrcs|sort(attribute='name') -%}
@main.group()
@click.pass_obj
def {{ rsrc["name"] }}(ctx_obj):
    """High level command for an {{ rsrc["name"] }}."""
    _LOGGER.debug(f"ctx_obj: {ctx_obj}")
    {% set clazz_name = rsrc["name"].capitalize() -%}
    {% if "_" in clazz_name -%}
    {% set new_clazz_name = [] -%}
    {% for part in  clazz_name.split("_") -%}
        {% set _ = new_clazz_name.append(part.capitalize()) -%}
    {% endfor -%}
    {% set clazz_name = "".join(new_clazz_name) -%}
    {% endif -%}
    ctx_obj["api_obj"] = {{ rsrc["name"] }}_api.{{ clazz_name }}Api(api_client=ctx_obj["api_client"])


# pylint: disable=redefined-builtin
{# high-level resource operations -#}
{% for op in rsrc["operations"]["resource"]|sort(attribute='name') -%}
@{{ rsrc["name"] }}.command("{{ op["name"] }}")
{% for attr in op["attrs"]|sort(attribute='name') -%}
@click.option("--{{ attr["name"].replace("_", "-") }}", help="{{ attr["description"] }}", type={{ attr["type"] }}, required={{ attr["required"] }})
{% endfor -%}
@click.pass_obj
@firestone_utils.click_coro
@api_exc
async def {{ op["id"] }}(ctx_obj{% for attr in op["attrs"]|sort(attribute='name') -%}{{ ", " + attr["name"].replace("-", "_") }}{% endfor -%}):
    """{{ op["description"] }}"""
    api_obj = ctx_obj["api_obj"]
    params = {
        {% for attr in op["attrs"]|sort(attribute='name') -%}
        "{{ attr["name"] }}":  {{ attr["name"].replace("-", "_") }},
        {% endfor %}
    }
    {% set comp_name = rsrc["name"] if not rsrc["name"].endswith("s") else rsrc["name"][:-1] -%}
    {% if op["name"] == "create" -%}

    {% set clazz_name = comp_name.capitalize() -%}
    {% if "_" in clazz_name -%}
    {% set new_clazz_name = [] -%}
    {% for part in  clazz_name.split("_") -%}
        {% set _ = new_clazz_name.append(part.capitalize()) -%}
    {% endfor -%}
    {% set clazz_name = "".join(new_clazz_name) -%}
    {% endif -%}

    req_body = create_{{ comp_name }}_model.Create{{ clazz_name }}(**params)
    resp = await api_obj.{{ op["id"] }}(req_body)
    {% else %}
    resp = await api_obj.{{ op["id"] }}(**params)
    {% endif -%}
    _LOGGER.debug(f"resp: {resp}")

    if isinstance(resp, list):
        click.echo(json.dumps([obj.to_dict() for obj in resp]))
        return

    if resp:
        click.echo(json.dumps(resp.to_dict()))
        return

    click.echo("No data returned")

{% endfor -%}


{# high-level resource instance operations -#}
{% for op in rsrc["operations"]["instance"]|sort(attribute='name') -%}
@{{ rsrc["name"] }}.command("{{ op["name"] }}")
{% for attr in op["attrs"]|sort(attribute='name') -%}
{% if attr.get("argument") -%}
@click.argument("{{ attr["name"] }}", type={{ attr["type"] }})
{% else -%}
@click.option("--{{ attr["name"].replace("_", "-") }}", help="{{ attr["description"] }}", type={{ attr["type"] }}, required={{ attr["required"] }})
{% endif -%}
{% endfor -%}
@click.pass_obj
@firestone_utils.click_coro
@api_exc
async def {{ op["id"] }}(ctx_obj{% for attr in op["attrs"]|sort(attribute='name') -%}{{ ", " + attr["name"].replace("-", "_") }}{% endfor -%}):
    """{{ op["description"] }}"""
    api_obj = ctx_obj["api_obj"]
    params = {
        {% for attr in op["attrs"]|sort(attribute='name') -%}
        {% if not attr.get("argument") -%}
        "{{ attr["name"] }}":  {{ attr["name"].replace("-", "_") }},
        {% endif -%}
        {% endfor %}
    }
    {% set comp_name = rsrc["name"] if not rsrc["name"].endswith("s") else rsrc["name"][:-1] %}
    {% if op["name"] == "create" %}
    req_body = create_{{ comp_name }}_model.{{ comp_name.capitalize(**params) }}()
    {% elif op["name"] == "update" %}
    req_body = update_{{ comp_name }}_model.Update{{ comp_name.capitalize() }}(**params)
    resp = await api_obj.{{ op["id"] }}(
        {% for attr in op["attrs"]|sort(attribute='name') -%}
            {% if attr.get("argument") -%}{{ attr["name"] }}, {% endif -%}
        {% endfor -%}
        req_body
    )
    {% else %}
    resp = await api_obj.{{ op["id"] }}(
        {% for attr in op["attrs"]|sort(attribute='name') -%}
            {% if attr.get("argument") -%}{{ attr["name"] }}, {% endif -%}
        {% endfor -%}
        **params
    )
    {% endif -%}
    _LOGGER.debug(f"resp: {resp}")

    if isinstance(resp, list):
        print(json.dumps([obj.to_dict() for obj in resp]))
        return

    print(json.dumps(resp.to_dict()) if resp else "None")


{% endfor -%}
{% endfor -%}


if __name__ == "__main__":
    # pylint: disable=no-value-for-parameter
    main()
