"""Azure Image Creation, Management, Testing"""

import json
import re
import sys
import urllib
from copy import deepcopy

import pkg_resources
import yaml

import gen
import gen.build_deploy.util as util
import gen.template
import pkgpanda.build
from gen.internals import Late, Source
from pkgpanda.util import split_by_token

# TODO(cmaloney): Make it so the template only completes when services are properly up.
late_services = ""

ILLEGAL_ARM_CHARS_PATTERN = re.compile("[']")

DOWNLOAD_URL_TEMPLATE = ("{download_url}{reproducible_artifact_path}/azure/{arm_template_name}")

INSTANCE_GROUPS = {
    'master': {
        'report_name': 'MasterServerGroup',
        'roles': ['master', 'azure_master']
    },
    'slave': {
        'report_name': 'SlaveServerGroup',
        'roles': ['slave']
    },
    'slave_public': {
        'report_name': 'PublicSlaveServerGroup',
        'roles': ['slave_public']
    }
}


def validate_provider(provider):
    assert provider == 'azure'


azure_base_source = Source(entry={
    'validate': [
        validate_provider
    ],
    'default': {
        'platform': 'azure',
        'enable_docker_gc': 'true'
    },
    'must': {
        'resolvers': '["168.63.129.16"]',
        'ip_detect_contents': yaml.dump(pkg_resources.resource_string('gen', 'ip-detect/azure.sh').decode()),
        'master_discovery': 'static',
        'exhibitor_storage_backend': 'azure',
        'master_cloud_config': '{{ master_cloud_config }}',
        'slave_cloud_config': '{{ slave_cloud_config }}',
        'slave_public_cloud_config': '{{ slave_public_cloud_config }}',
        'fault_domain_detect_contents': yaml.dump(
            pkg_resources.resource_string('gen', 'fault-domain-detect/cloud.sh').decode())
    },
    'conditional': {
        'oauth_available': {
            'true': {
                'must': {
                    'oauth_enabled': Late("[[[variables('oauthEnabled')]]]"),
                    'adminrouter_auth_enabled': Late("[[[variables('oauthEnabled')]]]"),
                }
            },
            'false': {},
        },
        'licensing_enabled': {
            'true': {
                'must': {
                    'license_key_contents': Late("[[[variables('licenseKey')]]]"),
                },
                'secret': [
                    'license_key_contents',
                ],
            },
            'false': {},
        }
    }
})


def validate_cloud_config(cc_string):
    '''
    Validate that there aren't any single quotes present since they break the
    ARM template system. Exit with an error message if any invalid characters
    are detected.

    @param cc_string: str, Cloud Configuration
    '''
    if "'" in cc_string:
        print("ERROR: Illegal cloud config string detected.", file=sys.stderr)
        print("ERROR: {} contains a `'`".format(cc_string), file=sys.stderr)
        sys.exit(1)


def transform(cloud_config_yaml_str):
    '''
    Transforms the given yaml into a list of strings which are concatenated
    together by the ARM template system. We must make it a list of strings so
    that ARM template parameters appear at the top level of the template and get
    substituted.
    '''
    cc_json = json.dumps(yaml.safe_load(cloud_config_yaml_str), sort_keys=True)

    def _quote_literals(parts):
        for part, is_param in parts:
            if is_param:
                yield part
            else:
                validate_cloud_config(part)
                yield "'{}'".format(part)

    # We're embedding this as a json string.
    return json.dumps(
        "[base64(concat('#cloud-config\n\n', " +
        ", ".join(_quote_literals(split_by_token('[[[', ']]]', cc_json, strip_token_decoration=True))) +
        "))]"
    )


def render_arm(
        arm_template,
        master_cloudconfig_yaml_str,
        slave_cloudconfig_yaml_str,
        slave_public_cloudconfig_yaml_str):

    template_str = gen.template.parse_str(arm_template).render({
        'master_cloud_config': transform(master_cloudconfig_yaml_str),
        'slave_cloud_config': transform(slave_cloudconfig_yaml_str),
        'slave_public_cloud_config': transform(slave_public_cloudconfig_yaml_str)
    })

    # Add in some metadata to help support engineers
    template_json = json.loads(template_str)
    template_json['variables']['DcosImageCommit'] = util.dcos_image_commit
    template_json['variables']['TemplateGenerationDate'] = util.template_generation_date
    return json.dumps(template_json)


def gen_templates(gen_arguments, arm_template, extra_sources):
    '''
    Render the cloud_config template given a particular set of options

    @param user_args: dict, args to pass to the gen library. These are user
                     input arguments which get filled in/prompted for.
    @param arm_template: string, path to the source arm template for rendering
                         by the gen library (e.g. 'azure/templates/azuredeploy.json')
    '''
    results = gen.generate(
        arguments=gen_arguments,
        extra_templates=['azure/cloud-config.yaml', 'azure/templates/' + arm_template + '.json'],
        extra_sources=[azure_base_source] + extra_sources)

    cloud_config = results.templates['cloud-config.yaml']

    # Add general services
    cloud_config = results.utils.add_services(cloud_config, 'canonical')